//go:build homework

package homework

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"reflect"
	"strings"
)

var errInvalidEntity = errors.New("invalid entity")

func contains(s []string, ele string) bool {
	for _, e := range s {
		if e == ele {
			return true
		}
	}
	return false
}

func formatFields(fields []string) []string {
	fdCnt := len(fields)
	fmtFields := make([]string, 0, fdCnt)
	for _, ele := range fields[:] {
		fmtFields = append(fmtFields, fmt.Sprintf("`%s`", ele))
	}

	return fmtFields
}

func genSql(tbName string, fileds []string, val []string) string {
	prefix := "INSERT INTO "

	sql := strings.Builder{}
	sql.WriteString(prefix)

	fmt.Fprintf(&sql, "`%s`(", tbName)
	fmtFields := formatFields(fileds[:])
	sql.WriteString(strings.Join(fmtFields, ","))
	sql.WriteString(")")

	sql.WriteString(" VALUES(")
	sql.WriteString(strings.Join(val[:], ","))
	sql.WriteString(");")

	return sql.String()
}

func recurTyp(typ reflect.Type, val reflect.Value) ([]string, []string, []interface{}, error) {
	if typ.Kind() != reflect.Struct || (typ.Kind() == reflect.Pointer && typ.Elem().Kind() != reflect.Struct) {
		return nil, nil, nil, errInvalidEntity
	}

	if typ.Kind() == reflect.Pointer && typ.Elem().Kind() == reflect.Struct {
		typ = typ.Elem()
		val = val.Elem()
	}

	numFiled := typ.NumField()
	fdNameSlice := make([]string, 0, numFiled)
	valSlice := make([]string, 0, numFiled)
	var args []interface{}

	for i := 0; i < numFiled; i++ {
		fd := typ.Field(i)

		// case: not embed field
		if !fd.Anonymous {
			fdNameSlice = append(fdNameSlice, fd.Name)
			valSlice = append(valSlice, "?")
			args = append(args, val.Field(i).Interface())
			continue
		}

		// Check whether fd implements Scan and Value interface
		// scannerTyp := reflect.TypeOf((*sql.Scanner)(nil)).Elem()
		valuerTyp := reflect.TypeOf((*driver.Valuer)(nil)).Elem()

		if fd.Type.Kind() == reflect.Struct && !fd.Type.Implements(valuerTyp) {
			val := val.Field(i)

			recurFdNameSlice, recurValSlice, recurArgs, err := recurTyp(fd.Type, val)
			if err != nil {
				return nil, nil, nil, errInvalidEntity
			}

			for idx, ele := range recurFdNameSlice {
				if !contains(fdNameSlice, ele) {
					fdNameSlice = append(fdNameSlice, ele)
					valSlice = append(valSlice, recurValSlice[idx])
					args = append(args, recurArgs[idx])
				}
			}

			continue
		}

		if !contains(fdNameSlice, fd.Name) {
			fdNameSlice = append(fdNameSlice, fd.Name)
			valSlice = append(valSlice, "?")
			args = append(args, val.Field(i).Interface())
		}
	}

	return fdNameSlice, valSlice, args, nil
}

// InsertStmt 作业里面我们这个只是生成 SQL，所以在处理 sql.NullString 之类的接口
// 只需要判断有没有实现 driver.Valuer 就可以了
func InsertStmt(entity interface{}) (string, []interface{}, error) {
	// nil
	if entity == nil {
		return "", nil, errInvalidEntity
	}

	typ := reflect.TypeOf(entity)
	val := reflect.ValueOf(entity)

	// empty struct
	if typ.Kind() == reflect.Struct && typ.NumField() == 0 {
		return "", nil, errInvalidEntity
	}

	// multiple pointer
	if typ.Kind() == reflect.Pointer && typ.Elem().Kind() == reflect.Pointer {
		return "", nil, errInvalidEntity
	}

	if typ.Kind() == reflect.Pointer && typ.Elem().Kind() == reflect.Struct {
		typ = typ.Elem()
		val = val.Elem()
	}

	var args []interface{}
	fdNameSlice, valSlice, args, err := recurTyp(typ, val)
	if err != nil {
		return "", nil, err
	}

	sql := strings.Builder{}
	tbName := typ.Name()
	sql.WriteString(genSql(tbName, fdNameSlice, valSlice))

	return sql.String(), args, nil

	// val := reflect.ValueOf(entity)
	// typ := val.Type()
	// 检测 entity 是否符合我们的要求
	// 我们只支持有限的几种输入

	// 使用 strings.builder 来拼接 字符串
	// bd := strings.builder{}

	// 构造 INSERT INTO XXX，XXX 是你的表名，这里我们直接用结构体名字

	// 遍历所有的字段，构造出来的是 INSERT INTO XXX(col1, col2, col3)
	// 在这个遍历的过程中，你就可以把参数构造出来
	// 如果你打算支持组合，那么这里你要深入解析每一个组合的结构体
	// 并且层层深入进去

	// 拼接 VALUES，达成 INSERT INTO XXX(col1, col2, col3) VALUES

	// 再一次遍历所有的字段，要拼接成 INSERT INTO XXX(col1, col2, col3) VALUES(?,?,?)
	// 注意，在第一次遍历的时候我们就已经拿到了参数的值，所以这里就是简单拼接 ?,?,?

	// return bd.String(), args, nil
	// panic("implement me")
}
